package service

import (
	"github.com/jinzhu/gorm"
	"reflect"
	"time"
	"yak.admin/src/util"
)

type ReaderQuery struct {
	Debug bool
	table string
	pagesize int
	page int
	query *gorm.DB
	preloads []string
	util.Formatter
}

func (reader *ReaderQuery) WithPreloads(arg []string) *ReaderQuery {
	reader.preloads = arg
	return reader
}

func (reader *ReaderQuery) GetPage() int {
	return reader.page
}

func (reader *ReaderQuery) GetPagesize() int {
	return reader.pagesize
}

func (reader *ReaderQuery) CreateModelQuery(model interface{}) *ReaderQuery {
	db := util.GetMysqlPool().Handler()
	reader.query = db.Model(model)
	return reader
}

func (reader *ReaderQuery) Related(value interface{}, foreignKeys ...string) *ReaderQuery {
	reader.query = reader.query.Related(value, foreignKeys...)
	return reader
}


func (reader *ReaderQuery) CreateQuery(table string) *ReaderQuery {
	reader.table = table
	db := util.GetMysqlPool().Handler()
	reader.query = db.Table(table)
	return reader
}

func (reader *ReaderQuery) WithPageSize(pagesize int) *ReaderQuery {
	reader.pagesize = pagesize
	return reader
}

func (reader *ReaderQuery) WithPage(page int) *ReaderQuery {
	reader.page = page
	return reader
}

func (reader *ReaderQuery) WithYakLst(key string, value string, oper string) *ReaderQuery {
	if !reader.IsEmpty(key) && !reader.IsEmpty(value) {
		switch oper {
		case "EQ":
			return reader.WithEQ(key, value)
		default:
			return reader.WithLike(key, value)
		}
	}
	return reader
}

func (reader *ReaderQuery) WithYakDtIntDate(key string, start string, end string, format string) *ReaderQuery {
	if !reader.IsEmpty(key) {
		loc, _ := time.LoadLocation("Local")
		if !reader.IsEmpty(start) {
			if s, err := time.ParseInLocation(format, start, loc); err == nil {
				reader.WithGTE(key, s.Unix())
			}
		}

		if !reader.IsEmpty(end) {
			if e, err := time.ParseInLocation(format, end, loc); err == nil {
				reader.WithLTE(key, e.Unix() + 86400)
			}
		}
	}
	return reader
}

func (reader *ReaderQuery) WithYakDt(key string, start string, end string) *ReaderQuery {
	if !reader.IsEmpty(key) {
		reader.WithGTE(key, start)
		reader.WithLTE(key, end)
	}
	return reader
}

func (reader *ReaderQuery) WithLTE(c string, v interface{}) *ReaderQuery {
	if !reader.IsEmpty(v) {
		reader.query = reader.query.Where(c + " <= ?", v)
	}
	return reader
}

func (reader *ReaderQuery) WithGTE(c string, v interface{}) *ReaderQuery {
	if !reader.IsEmpty(v) {
		reader.query = reader.query.Where(c + " >= ?", v)
	}
	return reader
}

func (reader *ReaderQuery) WithPCC(province string, city string, county string, super bool) *ReaderQuery {
	if !super {
		return reader.WithEQ("province", province).WithEQ("city", city).WithEQ("county", county)
	}
	return reader
}

func (reader *ReaderQuery) WithLike(c string, v interface{}) *ReaderQuery {
	if !reader.IsEmpty(v) {
		reader.query = reader.query.Where("`" + c + "` LIKE ?", "%" + v.(string) + "%")
	}
	return reader
}

func (reader *ReaderQuery) WithIN(c string, v interface{}) *ReaderQuery {
	reader.query = reader.query.Where("`" + c + "` in (?)", v)
	return reader
}

func (reader *ReaderQuery) WithEQ(c string, v interface{}) *ReaderQuery {
	if !reader.IsEmpty(v) {
		reader.query = reader.query.Where("`" + c + "` = ?", v)
	}
	return reader
}

func (reader *ReaderQuery) WithFindInSet(c string, v interface{}) *ReaderQuery {
	if !reader.IsEmpty(v) {
		reader.query = reader.query.Where("find_in_set(?, " + c + ")", v)
	}
	return reader
}

func (reader *ReaderQuery) Reader(container interface{}) *gorm.DB {
	query := reader.query.Offset((reader.page - 1) * reader.pagesize).Limit(reader.pagesize)
	for _, preload := range reader.preloads {
		query = query.Preload(preload)
	}
	if reader.Debug {
		return query.Debug().Find(container)
	}
	return query.Find(container)
}

func (reader *ReaderQuery) One(container interface{}) *gorm.DB {
	query := reader.query.Limit(1)
	for _, preload := range reader.preloads {
		query = query.Preload(preload)
	}
	if reader.Debug {
		return query.Debug().Find(container)
	}
	return query.First(container)
}

func (reader *ReaderQuery) Count() int64 {
	var count int64
	reader.query.Count(&count)
	return count
}

func (reader *ReaderQuery) SerialStart() int {
	return (reader.page - 1) * reader.pagesize
}

func (reader *ReaderQuery) WithOrder(c string, sort string) *ReaderQuery {
	if sort == "asc" || sort == "desc"{
		reader.query = reader.query.Order( c + " " + sort)
	}
	return reader
}

func (reader *ReaderQuery) WithDefaultOrder(c string, sort string, dc string, dsort string) *ReaderQuery {
	if sort == "" {
		sort = dsort
		c = dc
	}
	reader.query = reader.query.Order( c + " " + sort)
	return reader
}

func (reader *ReaderQuery) IsEmpty(v interface{}) bool {
	t := reflect.TypeOf(v)
	switch t.String() {
	case "uint":
		return v == 0
	case "int":
		return v == 0
	case "int8":
		return v == 0
	case "int16":
		return v == 0
	case "int32":
		return v == 0
	case "int64":
		return v == 0
	case "string":
		return v == ""
	default:
		return true
	}
}

func (reader *ReaderQuery) Datalist(arg interface{}, len int, fun func(index int, item interface{}) map[string]interface{}) []map[string]interface{}  {
	res := make([]map[string]interface{}, 0, len)
	slice, ok := reader.CreateAnyTypeSlice(arg)
	if !ok {
		return res
	}

	for index, value := range slice {
		res = append(res, fun(index + 1, value))
	}

	return res
}

func (reader *ReaderQuery) AsListField(arg interface{}, len int, field string) []interface{} {
	res := make([]interface{}, 0, len)
	slice, ok := reader.CreateAnyTypeSlice(arg)
	if !ok {
		return res
	}

	for _, value := range slice {
		immutable := reflect.ValueOf(value)
		val := immutable.FieldByName(field).Interface()
		res = append(res, val)
	}

	return res
}

func (reader *ReaderQuery) AsListImg(arg interface{}, len int, field string) []interface{} {
	res := make([]interface{}, 0, len)
	slice, ok := reader.CreateAnyTypeSlice(arg)
	if !ok {
		return res
	}

	for _, value := range slice {
		immutable := reflect.ValueOf(value)
		val := immutable.FieldByName(field).Interface()
		res = append(res, util.GetImage(val.(string)))
	}

	return res
}

func (reader *ReaderQuery) Viewlist(result interface{}, fun func( item interface{}) map[string]interface{}) map[string]interface{}  {
	return fun(result)
}

func (reader *ReaderQuery) CreateAnyTypeSlice(slice interface{}) ([]interface{}, bool) {
	val, ok := reader.isSlice(slice)
	if !ok {
		return nil, false
	}
	sliceLen := val.Len()
	out := make([]interface{}, sliceLen)
	for i := 0; i < sliceLen; i++ {
		out[i] = val.Index(i).Interface()
	}
	return out, true
}

func (reader *ReaderQuery) isSlice(arg interface{}) (val reflect.Value, ok bool) {
	val = reflect.ValueOf(arg)
	if val.Kind() == reflect.Slice {
		ok = true
	}
	return
}

func (reader *ReaderQuery) AsSerial(index int) int {
	return (reader.page - 1) * reader.pagesize + index
}


